package com.qfmy.web.app.service.impl.novel;

import com.qfmy.common.constant.RedisConstant;
import com.qfmy.model.entity.novel.NovelCarousel;
import com.qfmy.model.vo.novel.NovelCarouselVo;
import com.qfmy.web.app.mapper.novel.NovelCarouselMapper;
import com.qfmy.web.app.service.novel.NovelCarouselService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author 清风明月
 * @Package com.qfmy.web.app.service.impl.novel
 * @date 2025/10/31
 * @description 小说轮播图
 */
@Slf4j
@Service
public  class NovelCarouselServiceImpl implements NovelCarouselService {

    /**
     * 注入mapper
     */
    @Autowired
    private NovelCarouselMapper novelCarouselMapper;

    /**
     * 注入redisTemplate
     */
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 获取小说轮播
     * @param position
     * @return
     */
    @Override
    public List<NovelCarouselVo> getVerticalCarousel(Integer position) {
        //创建一个轮播图的缓存key
        String key = RedisConstant.APP_CAROUSEL_PREFIX + position;
        //获取缓存
        List<NovelCarouselVo> cachedNovels = getFromCache(key);
        if (cachedNovels != null) {
            return cachedNovels;
        }
        //缓存为空,获取锁从数据库里面查询
        String lock = RedisConstant.APP_CAROUSEL_PREFIX + "lock";
        String lockValue = UUID.randomUUID().toString();
        Boolean isLockAcquired = false;
        Thread renewalThread = null;
        try {
            //尝试获取锁
            isLockAcquired = redisTemplate.opsForValue().setIfAbsent(lock, lockValue, 3, TimeUnit.SECONDS);
            //判断是否成功获取
            if(isLockAcquired) {
                //启动看门狗线程,对锁进行续期
                renewalThread = startLockRenewal(lock, lockValue);
                //再次获取缓存
                cachedNovels = getFromCache(key);
                if (cachedNovels != null) {
                    return cachedNovels;
                }
                //还是获取不到，从数据库里面查询
                List<NovelCarousel> novelCarousels = novelCarouselMapper.getVerticalCarousel(position);
                cachedNovels = convertToVo(novelCarousels);
                //将缓存到redis
                Long ttl =  cachedNovels.isEmpty() ? 1L : 24L;
                redisTemplate.opsForValue().set(key,  cachedNovels, ttl, TimeUnit.HOURS);
                return  cachedNovels;
            }else{
                // 未获取到锁，重试（限制重试次数，避免无限循环）
                return retryGetRank(key);
            }
        } catch (Exception e) {
            log.error("获取锁异常", e);
            // 异常时返回空列表或降级处理，避免级联失败
            return Collections.emptyList();
        } finally {
            //停止续期线程
            if(renewalThread != null)
            {
                renewalThread.interrupt();
            }
            //释放锁
            if(isLockAcquired)
            {
                releaseLockSafely(lock, lockValue);
            }
        }
    }

    /**
     * 转换数据库实体为VO
     * @param novelCarousels
     * @return
     */
    private List<NovelCarouselVo> convertToVo(List<NovelCarousel> novelCarousels) {
        if (novelCarousels == null || novelCarousels.isEmpty()) {
            return Collections.emptyList();
        }
        return novelCarousels.stream().map(novelCarousel -> {
            NovelCarouselVo novelCarouselVo = new NovelCarouselVo();
            BeanUtils.copyProperties(novelCarousel, novelCarouselVo);
            return novelCarouselVo;
        }).collect(Collectors.toList());
    }

    /**
     * 安全释放锁（验证持有者）
     */
    private void releaseLockSafely(String lockKey, String expectedValue) {
        try {
            // 验证锁持有者
            String currentValue = (String) redisTemplate.opsForValue().get(lockKey);
            // 锁持有者验证成功，释放锁
            if (expectedValue.equals(currentValue)) {
                redisTemplate.delete(lockKey);
            }
        } catch (Exception e) {
            log.error("释放锁异常", e);
        }
    }

    /**
     * 启动锁续期线程（看门狗机制）
     */
    private Thread startLockRenewal(String lockKey, String lockValue) {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    // 每1秒续期一次，延长至3秒
                    redisTemplate.opsForValue().setIfPresent(lockKey, lockValue, 3, TimeUnit.SECONDS);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                } catch (Exception e) {
                    log.error("锁续期异常", e);
                    break;
                }
            }
        });
        thread.setDaemon(true); // 守护线程，随主线程退出
        thread.start();
        return thread;
    }

    /**
     * 从缓存获取数据并处理类型转换
     */
    private List<NovelCarouselVo> getFromCache(String redisKey) {
        try {
            Object cachedValue = redisTemplate.opsForValue().get(redisKey);
            if (cachedValue instanceof List<?>) {
                return (List<NovelCarouselVo>) cachedValue;
            }
        } catch (Exception e) {
            log.warn("缓存查询异常", e);
        }
        return null;
    }



    /**
     * 有限次数重试获取轮播图
     */
    private List<NovelCarouselVo> retryGetRank(String redisKey) throws InterruptedException {
        int maxRetries = 3;
        int retryCount = 0;
        long initialDelay = 50; // 初始延迟50ms

        while (retryCount < maxRetries) {
            Thread.sleep(initialDelay * (1 << retryCount)); // 指数退避策略
            List<NovelCarouselVo> result = getFromCache(redisKey);
            if (result != null) {
                return result;
            }
            retryCount++;
        }
        // 重试多次仍无结果，直接返回空对象
        return Collections.emptyList();
    }


}
